extends Control

enum SAVE_STATES{
	NONE,
	SAVEONE,
	SAVEALL
}
enum MOUSE_STATES{
	IDLE,
	MOVE,
	DRAW,
	ERASE
}
enum PEN_STATES{
	DOT,
	LINE,
	RECT
}
var save_state = SAVE_STATES.NONE
var mouse_state = MOUSE_STATES.IDLE
var pen_state = PEN_STATES.DOT

var button2state = {
	BUTTON_LEFT: MOUSE_STATES.DRAW,
	BUTTON_RIGHT: MOUSE_STATES.ERASE,
	BUTTON_MIDDLE: MOUSE_STATES.MOVE
}
var key2state = {
	KEY_CONTROL: PEN_STATES.RECT,
	KEY_SHIFT: PEN_STATES.LINE
}

var pen_start = Vector2.ZERO
var pen_stop = Vector2.ZERO

const CONFIG_PATH = "user://config.cfg"
var config = ConfigFile.new()
var config_loaded

const CACHE_PATH = "user://cache.dat"

var images:Array = []
var current_image:Image = null

var color_line = Color(0.5,0.5,0.5,0.3)
var color_pen = Color(0.5,0.5,0.5,0.5)

var canvas_dsize = 0.05
var canvas_max = pow(2,14)

var board_size
var image_size
var canvas_size
var pixel_size

# Files
onready var file_load:Button = $HSplit/VSplit/Files/Scroll/VBox/Load
onready var file_save:Button = $HSplit/VSplit/Files/Scroll/VBox/Save
onready var file_saveall:Button = $HSplit/VSplit/Files/Scroll/VBox/SaveAll
onready var auto_save:CheckButton = $HSplit/VSplit/Files/Scroll/VBox/AutoSave
onready var auto_save_time:SpinBox = $HSplit/VSplit/Files/Scroll/VBox/AutoSaveTime
onready var auto_timer:Timer = $AutoSave
onready var cache_state:Label = $HSplit/HSplit/PaintBoard/Canvas/CacheState
onready var cache_timer:Timer = $HSplit/HSplit/PaintBoard/Canvas/CacheStateTimer
# Frames
onready var frame:SpinBox = $HSplit/VSplit/Frames/Scroll/VBox/Frame
onready var frame_play:Button = $HSplit/VSplit/Frames/Scroll/VBox/HBox/Play
onready var frame_play_play:TextureRect = $HSplit/VSplit/Frames/Scroll/VBox/HBox/Play/Play
onready var frame_play_pause:TextureRect = $HSplit/VSplit/Frames/Scroll/VBox/HBox/Play/Pause
onready var frame_left:Button = $HSplit/VSplit/Frames/Scroll/VBox/HBox/Left
onready var frame_right:Button = $HSplit/VSplit/Frames/Scroll/VBox/HBox/Right
onready var frame_new:Button = $HSplit/VSplit/Frames/Scroll/VBox/New
onready var frame_copy:Button = $HSplit/VSplit/Frames/Scroll/VBox/Copy
onready var frame_remove:Button = $HSplit/VSplit/Frames/Scroll/VBox/Remove
onready var frame_timer:Timer = $HSplit/VSplit/Frames/Timer
# FPS
onready var fps:SpinBox = $HSplit/VSplit/Frames/Scroll/VBox/FPS
# Background
onready var back_panel:StyleBox = $HSplit/HSplit/PaintBoard.get("custom_styles/panel")
onready var back_color:ColorPickerButton = $HSplit/HSplit/PenSetting/Scroll/VBox/HBox/BackColor
# Pen
onready var pen_color:ColorPickerButton = $HSplit/HSplit/PenSetting/Scroll/VBox/HBox2/PenColor
onready var pen_size:SpinBox = $HSplit/HSplit/PenSetting/Scroll/VBox/PenSize
# Canvas
var texture:ImageTexture = ImageTexture.new()
onready var canvas:Panel = $HSplit/HSplit/PaintBoard/Canvas
onready var canvas_image:TextureRect = $HSplit/HSplit/PaintBoard/Panel/Image
onready var canvas_grid:CheckButton = $HSplit/HSplit/PenSetting/Scroll/VBox/Grid
onready var canvas_grid_gap:SpinBox = $HSplit/HSplit/PenSetting/Scroll/VBox/GridGap
# Image Size
onready var image_scale:SpinBox = $HSplit/HSplit/PenSetting/Scroll/VBox/ImgScale
# Popup
onready var popup_message:PopupDialog = $Message
onready var popup_message_timer:Timer = $Message/Timer
onready var popup_message_msg:Label = $Message/Container/VBox/Msg
onready var popup_message_tip:Label = $Message/Container/VBox/Tip
onready var popup_newframe:PopupDialog = $NewFrame
onready var popup_newframe_x:SpinBox = $NewFrame/Container/VBox/HBoxX/NewX
onready var popup_newframe_y:SpinBox = $NewFrame/Container/VBox/HBoxY/NewY
onready var popup_newframe_new:Button = $NewFrame/Container/VBox/NewFrame
onready var popup_clean:PopupDialog = $Clean
onready var popup_help:WindowDialog = $Help
onready var popup_help_auto:CheckButton = $Help/Container/VBox/AutoHelp
onready var popup_files:FileDialog = $Files

func _enter_tree():
	config_loaded = config.load(CONFIG_PATH)
	if config_loaded == OK:
		OS.window_size = config.get_value("layout","window_size",OS.window_size)
		OS.window_position = config.get_value("layout","window_position",OS.window_position)
		OS.window_maximized = config.get_value("layout","window_maximized",OS.window_maximized)

func _exit_tree():
	config.set_value("layout","window_maximized",OS.window_maximized)
	if OS.window_maximized:
		OS.window_maximized = false
	config.set_value("layout","window_size",OS.window_size)
	config.set_value("layout","window_position",OS.window_position)
	config.set_value("canvas","pen_size",pen_size.value)
	config.set_value("canvas","pen_color",pen_color.color)
	config.set_value("canvas","back_color",back_color.color)
	config.set_value("canvas","grid",canvas_grid.pressed)
	config.set_value("canvas","grid_gap",canvas_grid_gap.value)
	config.set_value("file","current_dir",popup_files.current_dir)
	config.set_value("file","auto_save",auto_save.pressed)
	config.set_value("file","auto_save_time",auto_save_time.value)
	config.set_value("frame","new_x",popup_newframe_x.value)
	config.set_value("frame","new_y",popup_newframe_y.value)
	config.set_value("help","help_size",popup_help.rect_size)
	config.set_value("help","help_auto",popup_help_auto.pressed)
	config.save(CONFIG_PATH)

func reset_canvas():
	var min_length = min(canvas.rect_size.x,canvas.rect_size.y) * 0.9
	canvas_image.rect_size = Vector2(min_length,min_length)
	canvas_image.rect_position = canvas.rect_position + (canvas.rect_size-canvas_image.rect_size)/2

func _ready():
	if config_loaded == OK:
		pen_size.value = config.get_value("canvas","pen_size",pen_size.value)
		pen_color.color = config.get_value("canvas","pen_color",pen_color.color)
		back_color.color = config.get_value("canvas","back_color",back_color.color)
		canvas_grid.pressed = config.get_value("canvas","grid",canvas_grid.pressed)
		canvas_grid_gap.value = config.get_value("canvas","grid_gap",canvas_grid_gap.value)
		popup_files.current_dir = config.get_value("file","current_dir",popup_files.current_dir)
		auto_save.pressed = config.get_value("file","auto_save",auto_save.pressed)
		auto_save_time.value = config.get_value("file","auto_save_time",auto_save_time.value)
		popup_newframe_x.value = config.get_value("frame","new_x",popup_newframe_x.value)
		popup_newframe_y.value = config.get_value("frame","new_y",popup_newframe_y.value)
		popup_help.rect_size = config.get_value("help","help_size",popup_help.rect_size)
		popup_help_auto.pressed = config.get_value("help","help_auto",popup_help_auto.pressed)
	back_panel.bg_color = back_color.color
	if auto_save.pressed:
		var file = File.new()
		if file.open(CACHE_PATH,File.READ) == OK:
			images = file.get_var(true)
			file.close()
	auto_timer.wait_time = auto_save_time.value
	auto_timer.start()
	frame.max_value = images.size()-1
	frame.value = frame.max_value
	if images.size()>0:
		frame.min_value = 0
	reset_canvas()
	popup_message_tip.text = "%s 秒后自动关闭"%popup_message_timer.wait_time
	if popup_help_auto.pressed:
		popup_help.popup_centered()

func _on_BackColor_color_changed(color):
	back_panel.bg_color = color

func _on_Load_pressed():
	popup_files.mode = FileDialog.MODE_OPEN_FILES
	popup_files.window_title = "打开PNG"
	popup_files.popup_centered()

func _on_Save_pressed():
	if images.size()>0:
		save_state = SAVE_STATES.SAVEONE
		popup_files.mode = FileDialog.MODE_SAVE_FILE
		popup_files.window_title = "保存PNG"
		popup_files.popup_centered()

func _on_SaveAll_pressed():
	if images.size()>0:
		save_state = SAVE_STATES.SAVEALL
		popup_files.mode = FileDialog.MODE_SAVE_FILE
		popup_files.window_title = "保存全部"
		popup_files.popup_centered()

func _on_Files_popup_hide():
	save_state = SAVE_STATES.NONE

func set_frame_timer(new_state:bool):
	if images.size()>1 && new_state:
		if frame_timer.is_stopped():
			frame_timer.start(1.0 / fps.value)
	else:
		frame_timer.stop()
	frame_play_play.visible = frame_timer.is_stopped()
	frame_play_pause.visible = !frame_timer.is_stopped()

func move_frame(dir:int):
	var count_images = images.size()
	if count_images>1:
		var new_index = (int(frame.value)+count_images+dir)%count_images
		images[int(frame.value)] = images[new_index]
		images[new_index] = current_image
		frame.value = new_index

func _on_Left_pressed():
	move_frame(-1)

func _on_Right_pressed():
	move_frame(1)

func _on_New_pressed():
	if frame_timer.is_stopped():
		popup_newframe.popup_centered()

func _on_Remove_pressed():
	if images.size()>0 && frame_timer.is_stopped():
		images.remove(int(frame.value))
		if images.size()==0:
			frame.min_value = -1
		frame.value -= 1
		frame.max_value = images.size()-1

func _on_Copy_pressed():
	if images.size()>0 && frame_timer.is_stopped():
		images.insert(int(frame.value),current_image.duplicate())
		frame.max_value = images.size()-1
		frame.value += 1

func _on_Frame_value_changed(value):
	if value<0:
		canvas_image.texture = null
	else:
		current_image = images[int(value)]
		texture.create_from_image(current_image)
		texture.flags = 0
		canvas_image.texture = texture
	set_canvas_size()
	canvas.update()

func _on_Play_pressed():
	set_frame_timer(frame_play_play.visible)

func _on_FrameTimer_timeout():
	var count_images = images.size()
	if count_images>1:
		frame.value = (int(frame.value)+count_images+1)%count_images

func _on_NewFrame_pressed():
	if frame_timer.is_stopped():
		frame.max_value += 1
		current_image = Image.new()
		current_image.create(int(popup_newframe_x.value),int(popup_newframe_y.value),false,Image.FORMAT_RGBA8)
		current_image.fill(Color(0,0,0,0))
		images.insert(int(frame.value)+1,current_image)
		frame.value+=1
		frame.min_value = 0
		frame.max_value = images.size()-1

func _on_Message_about_to_show():
	if popup_message_timer.is_stopped():
		popup_message_timer.start()

func _on_MessageTimer_timeout():
	popup_message.hide()

func _on_Message_popup_hide():
	popup_message_timer.stop()

func _on_Files_files_selected(paths):
	var count_succeed = 0
	var file = File.new()
	for path in paths:
		if file.open(path,File.READ) == OK:
			var buffer = file.get_buffer(file.get_len())
			var image_loading = Image.new()
			if image_loading.load_png_from_buffer(buffer) == OK:
				images.append(image_loading)
				count_succeed += 1
	frame.max_value = images.size()-1
	frame.value = frame.max_value
	if images.size()>0:
		frame.min_value = 0
	popup_message_msg.text = "成功打开 %d 个文件（失败 %d 个）"%[count_succeed,paths.size()-count_succeed]
	popup_message.popup_centered()

func _on_Files_file_selected(path:String):
	var count_images = images.size()
	if count_images>0:
		var count_succeed = 0
		match save_state:
			SAVE_STATES.NONE:
				return
			SAVE_STATES.SAVEONE:
				count_images = 1
				if current_image.save_png(path) == OK:
					count_succeed += 1
			SAVE_STATES.SAVEALL:
				var file_name = path.get_basename()
				var file_ext = path.get_extension()
				for f in count_images:
					var file_path = "%s_%d.%s"%[file_name,f,file_ext]
					if images[f].save_png(file_path) == OK:
						count_succeed += 1
		popup_message_msg.text = "成功保存 %d 个文件（失败 %d 个）"%[count_succeed,count_images-count_succeed]
		popup_message.popup_centered()

func _on_CloseHelp_pressed():
	popup_help.hide()

func _on_Grid_pressed():
	canvas.update()

func _on_GridGap_value_changed(_value):
	canvas.update()

func _on_Clean_pressed():
	if images.size()>0:
		current_image.fill(Color(0,0,0,0))
		texture.set_data(current_image)
	popup_clean.hide()

func _on_NoClean_pressed():
	popup_clean.hide()

func set_canvas_size():
	if current_image:
		board_size = canvas_image.rect_size
		image_size = current_image.get_size()
		var canvas_x = min(board_size.x,image_size.x*board_size.y/image_size.y)
		var canvas_y = min(board_size.y,image_size.y*board_size.x/image_size.x)
		canvas_size = Vector2(canvas_x,canvas_y)
		pixel_size = canvas_size / image_size
	canvas.update()

func canvas_resize(mouse_pos,dir):
	if min(canvas_image.rect_size.x,canvas_image.rect_size.y)>canvas_max and dir>0:
		return
	var relation_pos = mouse_pos-canvas_image.rect_position
	var fix_pos = relation_pos / canvas_image.rect_size
	var diff_size = canvas_image.rect_size*canvas_dsize
	canvas_image.rect_size += dir * diff_size
	canvas_image.rect_position -= dir * fix_pos * diff_size

func pos_fix(pos,fix_pixel=false) -> Vector2:
	var board_pos = canvas_image.rect_position
	var canvas_pos = board_pos + (board_size-canvas_size)/2
	var relative_pos = (pos-canvas_pos) / pixel_size
	if fix_pixel:
		return canvas_pos + relative_pos.round()*pixel_size
	else:
		return relative_pos

func fill_rect(rect_start:Vector2,rect_stop:Vector2):
	if not current_image:
		return
	var color_fill = Color(0,0,0,0)
	match mouse_state:
		MOUSE_STATES.DRAW:
			color_fill = pen_color.color
		MOUSE_STATES.ERASE:
			color_fill = Color(0,0,0,0)
		_:
			return
	rect_start = rect_start.round()
	rect_stop = rect_stop.round()
	var rect_start_x = clamp(min(rect_start.x,rect_stop.x),0,image_size.x)
	var rect_start_y = clamp(min(rect_start.y,rect_stop.y),0,image_size.y)
	var rect_stop_x = clamp(max(rect_start.x,rect_stop.x),0,image_size.x)
	var rect_stop_y = clamp(max(rect_start.y,rect_stop.y),0,image_size.y)
	current_image.lock()
	for x in range(rect_start_x,rect_stop_x):
		for y in range(rect_start_y,rect_stop_y):
			current_image.set_pixel(x,y,color_fill)
	current_image.unlock()

func fill_dot(dot_pos):
	var pos_diff = Vector2(.5, .5)*pen_size.value
	fill_rect(dot_pos-pos_diff,dot_pos+pos_diff)

func fill_line(line_start:Vector2,line_stop:Vector2):
	line_start = (line_start - Vector2(.5, .5)).round() + Vector2(.5, .5)
	line_stop = (line_stop - Vector2(.5, .5)).round() + Vector2(.5, .5)
	var line_length = line_stop - line_start
	var line_len_abs = line_length.abs()
	var count_dots = max(line_len_abs.x,line_len_abs.y)
	var line_dir = line_length / count_dots
	for dot in count_dots+1:
		fill_dot(line_start + dot*line_dir)

func _on_Canvas_draw():
	if mouse_state == MOUSE_STATES.MOVE:
		canvas.mouse_default_cursor_shape = Control.CURSOR_MOVE
	else:
		canvas.mouse_default_cursor_shape = Control.CURSOR_CROSS
	if images.size()==0:
		canvas.mouse_default_cursor_shape = Control.CURSOR_FORBIDDEN
		return
	var board_pos = canvas_image.rect_position
	var canvas_pos = board_pos + (board_size-canvas_size)/2
	if canvas_grid.pressed:
		canvas.draw_rect(Rect2(canvas_pos,canvas_size),color_line,false)
		var gap = canvas_grid_gap.value
		for x in range(1,int(image_size.x/gap) + int(int(image_size.x)%int(gap)!=0)):
			canvas.draw_line(canvas_pos+Vector2(x*gap*pixel_size.x,0),canvas_pos+Vector2(x*gap*pixel_size.x,canvas_size.y),color_line)
		for y in range(1,int(image_size.y/gap) + int(int(image_size.y)%int(gap)!=0)):
			canvas.draw_line(canvas_pos+Vector2(0,y*gap*pixel_size.y),canvas_pos+Vector2(canvas_size.x,y*gap*pixel_size.y),color_line)
	match [mouse_state,pen_state]:
		[MOUSE_STATES.IDLE,PEN_STATES.DOT]:
			var pen_size_draw = pixel_size*pen_size.value
			var pen_start_draw = pos_fix(pen_stop-pen_size_draw/2,true)
			var pen_stop_draw = pos_fix(pen_stop+pen_size_draw/2,true)
			canvas.draw_rect(Rect2(pen_start_draw,pen_stop_draw-pen_start_draw),color_pen)
		[MOUSE_STATES.DRAW,PEN_STATES.RECT],[MOUSE_STATES.ERASE,PEN_STATES.RECT]:
			var pen_start_draw = pos_fix(pen_start,true)
			var pen_stop_draw = pos_fix(pen_stop,true)
			canvas.draw_rect(Rect2(pen_start_draw,pen_stop_draw-pen_start_draw),color_pen)
		[MOUSE_STATES.DRAW,PEN_STATES.LINE],[MOUSE_STATES.ERASE,PEN_STATES.LINE]:
			var line_start_draw = pos_fix(pen_start - pixel_size/2,true)
			var line_stop_draw = pos_fix(pen_stop - pixel_size/2,true)
			canvas.draw_rect(Rect2(line_start_draw,pixel_size),color_pen)
			canvas.draw_rect(Rect2(line_stop_draw,pixel_size),color_pen)
			canvas.draw_line(pen_start,pen_stop,color_pen,2)

func _input(event):
	if event is InputEventKey:
		if not event.pressed:
			if key2state.has(event.scancode) && pen_state==key2state[event.scancode]:
				pen_state = PEN_STATES.DOT
				mouse_state = MOUSE_STATES.IDLE
		elif not event.echo:
			match event.scancode:
				KEY_R:
					if images.size()>0:
						reset_canvas()
				KEY_C:
					if images.size()>0:
						popup_clean.popup_centered()
				KEY_H:
					popup_help.popup_centered()
				_:
					if pen_state==PEN_STATES.DOT && key2state.has(event.scancode):
						pen_state = key2state[event.scancode]
		canvas.update()

func _on_Canvas_gui_input(event:InputEvent):
	if images.size()==0:
		return
	if event is InputEventMouseButton:
		pen_stop = event.position
		if not event.pressed:
			match pen_state:
				PEN_STATES.RECT:
					fill_rect(pos_fix(pen_start),pos_fix(pen_stop))
				PEN_STATES.LINE:
					fill_line(pos_fix(pen_start),pos_fix(pen_stop))
			if button2state.has(event.button_index) && mouse_state==button2state[event.button_index]:
				mouse_state = MOUSE_STATES.IDLE
		elif mouse_state==MOUSE_STATES.IDLE:
			if button2state.has(event.button_index):
				mouse_state = button2state[event.button_index]
			match event.button_index:
				BUTTON_WHEEL_UP:
					match pen_state:
						PEN_STATES.DOT:
							canvas_resize(event.position,1)
						PEN_STATES.RECT:
							pen_size.value += 1
						PEN_STATES.LINE:
							frame.value -= 1
				BUTTON_WHEEL_DOWN:
					match pen_state:
						PEN_STATES.DOT:
							canvas_resize(event.position,-1)
						PEN_STATES.RECT:
							pen_size.value -= 1
						PEN_STATES.LINE:
							frame.value += 1
				BUTTON_LEFT,BUTTON_RIGHT:
					pen_start = event.position
	if event is InputEventMouseMotion:
		pen_stop = event.position
		match [mouse_state,pen_state]:
			[MOUSE_STATES.MOVE,_]:
				canvas_image.rect_position += event.relative
			[MOUSE_STATES.DRAW,PEN_STATES.DOT],[MOUSE_STATES.ERASE,PEN_STATES.DOT]:
				fill_dot(pos_fix(pen_stop))
	texture.set_data(current_image)
	canvas.update()


func _on_AutoSaveTime_value_changed(value):
	auto_timer.stop()
	auto_timer.wait_time = value
	auto_timer.start()


func _on_AutoSave_timeout():
	if auto_save.pressed:
		var file = File.new()
		if file.open(CACHE_PATH,File.WRITE) == OK:
			file.store_var(images,true)
			file.close()
			cache_state.visible = true


func _on_CacheState_visibility_changed():
	if cache_state.visible:
		cache_timer.start()


func _on_CacheStateTimer_timeout():
	cache_state.visible = false


func _on_ZoomIn_pressed():
	if images.size()>0 && frame_timer.is_stopped():
		var scale = image_scale.value
		var new_size = current_image.get_size() * scale
		current_image.resize(int(new_size.x), int(new_size.y), 0)
		print(current_image.get_size())
		set_canvas_size()
		canvas.update()


func _on_ZoomOut_pressed():
	if images.size()>0 && frame_timer.is_stopped():
		var scale = image_scale.value
		var new_size = current_image.get_size() / scale
		current_image.resize(int(new_size.x), int(new_size.y), 0)
		print(current_image.get_size())
		set_canvas_size()
		canvas.update()
